www.gusucode.com > VC VMR视频采集与显示程序-源码程序 > VC VMR视频采集与显示程序-源码程序/code/VMR_Capture.cpp
////////////////////////////////////////////////////////////////////// // Download by http://www.NewXing.com // This class is designed to provide simple interface for // simultaneous Video Capture & Preview using DirectShow // ////////////////////////////////////////////////////////////////////// // // References: MS DirectShow Samples // // // VMR_Capture.cpp: implementation of the CVMR_Capture class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "VMR_Capture.h" #define RELEASE_POINTER(x) { if (x) x->Release(); x = NULL; } ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CVMR_Capture::CVMR_Capture() { CoInitialize(NULL); m_pGB = NULL; m_pMC = NULL; m_pME = NULL; m_pWC = NULL; m_pDF =NULL; m_pCamOutPin =NULL; m_pFrame=NULL; m_nFramelen=0; m_psCurrent=STOPPED; } CVMR_Capture::~CVMR_Capture() { CloseInterfaces(); CoUninitialize( ); } /* 构建滤波器链表,添加各个滤波器,链接并运行链表*/ HRESULT CVMR_Capture::Init(int iDeviceID,HWND hWnd, int iWidth, int iHeight) { HRESULT hr; //再次调用函数,释放已经建立的链表 CloseInterfaces(); // 创建IGraphBuilder hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGB); if (SUCCEEDED(hr)) { // 创建VMR并添加到Graph中 InitializeWindowlessVMR(hWnd); // 把指定的设备捆绑到一个滤波器 if(!BindFilter(iDeviceID, &m_pDF)) return S_FALSE; // 添加采集设备滤波器到Graph中 hr = m_pGB->AddFilter(m_pDF, L"Video Capture"); if (FAILED(hr)) return hr; // 获取捕获滤波器的引脚 IEnumPins *pEnum; m_pDF->EnumPins(&pEnum); hr |= pEnum->Reset(); hr |= pEnum->Next(1, &m_pCamOutPin, NULL); // 获取媒体控制和事件接口 hr |= m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC); hr |= m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME); // 设置窗口通知消息处理 //hr = pME->SetNotifyWindow((OAHWND)hWnd, WM_GRAPHNOTIFY, 0); // 匹配视频分辨率,对视频显示窗口设置 hr |= InitVideoWindow(hWnd,iWidth, iHeight); // 为捕获图像帧申请内存 m_nFramelen=iWidth*iHeight*3; m_pFrame=(BYTE*) new BYTE[m_nFramelen]; // 运行Graph,捕获视频 m_psCurrent = STOPPED; hr |= m_pGB->Render(m_pCamOutPin); hr |= m_pMC->Run(); if (FAILED(hr)) return hr; m_psCurrent = RUNNING; } return hr; } /* 创建VMR,添加、设置VMR */ HRESULT CVMR_Capture::InitializeWindowlessVMR(HWND hWnd) { IBaseFilter* pVmr = NULL; // 创建VMR HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr); if (SUCCEEDED(hr)) { //添加VMR到滤波器链表中 hr = m_pGB->AddFilter(pVmr, L"Video Mixing Renderer"); if (SUCCEEDED(hr)) { // 设置无窗口渲染模式 IVMRFilterConfig* pConfig; hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig); if( SUCCEEDED(hr)) { pConfig->SetRenderingMode(VMRMode_Windowless); pConfig->Release(); } // 设置传入的窗口为显示窗口 hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&m_pWC); if( SUCCEEDED(hr)) { m_pWC->SetVideoClippingWindow(hWnd); } } pVmr->Release(); } return hr; } /* 把指定的采集设备与Filter捆绑 */ bool CVMR_Capture::BindFilter(int deviceId, IBaseFilter **pFilter) { HRESULT hr; if (deviceId < 0) return false; // enumerate all video capture devices ICreateDevEnum *pCreateDevEnum; hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum); if (hr != NOERROR) return false; IEnumMoniker *pEm; hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0); if (hr != NOERROR) return false; pEm->Reset(); ULONG cFetched; IMoniker *pM; int index = 0; while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK, index <= deviceId) { IPropertyBag *pBag; hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag); if(SUCCEEDED(hr)) { VARIANT var; var.vt = VT_BSTR; hr = pBag->Read(L"FriendlyName", &var, NULL); if (hr == NOERROR) { if (index == deviceId) { pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter); } SysFreeString(var.bstrVal); } pBag->Release(); } pM->Release(); index++; } return true; } /* 设置捕获图像帧的格式,遍历所有格式是否有预定格式,若没有则以默认格式捕获 */ HRESULT CVMR_Capture::InitVideoWindow(HWND hWnd,int width, int height) { HRESULT hr; RECT rcDest; IAMStreamConfig *pConfig; IEnumMediaTypes *pMedia; AM_MEDIA_TYPE *pmt = NULL, *pfnt = NULL; hr = m_pCamOutPin->EnumMediaTypes( &pMedia ); if(SUCCEEDED(hr)) { //把所有视频的所有格式遍历一遍,看是否有预定的格式 while(pMedia->Next(1, &pmt, 0) == S_OK) { if( pmt->formattype == FORMAT_VideoInfo ) { VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pmt->pbFormat; // 当前的格式是否与预定格式相同,即宽和高相同 if( vih->bmiHeader.biWidth == width && vih->bmiHeader.biHeight == height ) { pfnt = pmt; break; } DeleteMediaType( pmt ); } } pMedia->Release(); } hr = m_pCamOutPin->QueryInterface( IID_IAMStreamConfig, (void **) &pConfig ); if(SUCCEEDED(hr)) { // 有预定的格式 if( pfnt != NULL ) { hr=pConfig->SetFormat( pfnt ); DeleteMediaType( pfnt ); } // 没有预定的格式,读取缺省媒体格式 hr = pConfig->GetFormat( &pfnt ); if(SUCCEEDED(hr)) { m_nWidth = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biWidth; //读取高 m_nHeight = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biHeight; //读取宽 DeleteMediaType( pfnt ); } } // 获取传入窗口的区域,以设置显示窗口 ::GetClientRect (hWnd,&rcDest); hr = m_pWC->SetVideoPosition(NULL, &rcDest); return hr; } /* 释放所有资源,断开链接 */ void CVMR_Capture::CloseInterfaces(void) { HRESULT hr; // 停止媒体回放 if(m_pMC) hr = m_pMC->Stop(); m_psCurrent = STOPPED; // 释放并清零接口指针 if(m_pCamOutPin) m_pCamOutPin->Disconnect(); RELEASE_POINTER(m_pCamOutPin); RELEASE_POINTER(m_pMC); RELEASE_POINTER(m_pGB); RELEASE_POINTER(m_pWC); RELEASE_POINTER(m_pDF); // 释放分配的内存 if(m_pFrame!=NULL) delete []m_pFrame; } /* 删除媒体类型 */ void CVMR_Capture::DeleteMediaType(AM_MEDIA_TYPE *pmt) { // allow NULL pointers for coding simplicity if (pmt == NULL) { return; } if (pmt->cbFormat != 0) { CoTaskMemFree((PVOID)pmt->pbFormat); pmt->cbFormat = 0; pmt->pbFormat = NULL; } if (pmt->pUnk != NULL) { pmt->pUnk->Release(); pmt->pUnk = NULL; } CoTaskMemFree((PVOID)pmt); } /* 把捕获的图像帧空间传出 */ DWORD CVMR_Capture::GetFrame(BYTE **pFrame) { if(m_pFrame && m_nFramelen) { *pFrame = m_pFrame; } return m_nFramelen; } /* 从VMR中获取一帧图像格式:RGB24/I420 */ DWORD CVMR_Capture::GrabFrame() { if(m_pWC ) { BYTE* lpCurrImage = NULL; if(m_pWC->GetCurrentImage(&lpCurrImage) == S_OK) { // 读取图像帧数据lpCurrImage LPBITMAPINFOHEADER pdib = (LPBITMAPINFOHEADER) lpCurrImage; if(m_pFrame==NULL || (pdib->biHeight * pdib->biWidth * 3) !=m_nFramelen ) { if(m_pFrame!=NULL) delete []m_pFrame; m_nFramelen = pdib->biHeight * pdib->biWidth * 3; m_pFrame = new BYTE [pdib->biHeight * pdib->biWidth * 3] ; } if(pdib->biBitCount == 32) { DWORD dwSize=0, dwWritten=0; BYTE *pTemp32; pTemp32=lpCurrImage + sizeof(BITMAPINFOHEADER); //change from 32 to 24 bit /pixel this->Convert24Image(pTemp32, m_pFrame, pdib->biSizeImage); } // 释放该图像lpCurrImage CoTaskMemFree(lpCurrImage); } else { return -1; } }else{ return -1; } return m_nFramelen; } /* ARGB32 to RGB24 */ bool CVMR_Capture::Convert24Image(BYTE *p32Img, BYTE *p24Img,DWORD dwSize32) { if(p32Img != NULL && p24Img != NULL && dwSize32>0) { DWORD dwSize24; dwSize24=(dwSize32 * 3)/4; BYTE *pTemp=p32Img,*ptr=p24Img; for (DWORD index = 0; index < dwSize32/4 ; index++) { unsigned char r = *(pTemp++); unsigned char g = *(pTemp++); unsigned char b = *(pTemp++); (pTemp++);//skip alpha *(ptr++) = b; *(ptr++) = g; *(ptr++) = r; } } else { return false; } return true; } /* 停止捕获 */ void CVMR_Capture::StopCapture() { HRESULT hr; if((m_psCurrent == PAUSED) || (m_psCurrent == RUNNING)) { LONGLONG pos = 0; hr = m_pMC->Stop(); m_psCurrent = STOPPED; // Display the first frame to indicate the reset condition hr = m_pMC->Pause(); } } /* 暂停 */ BOOL CVMR_Capture::Pause() { if (!m_pMC) return FALSE; if(((m_psCurrent == PAUSED) || (m_psCurrent == STOPPED)) ) { this->StopCapture(); if (SUCCEEDED(m_pMC->Run())) m_psCurrent = RUNNING; } else { if (SUCCEEDED(m_pMC->Pause())) m_psCurrent = PAUSED; } return TRUE; } /* 枚举本地系统的采集设备 */ int CVMR_Capture::EnumDevices(HWND hList) { if (!hList) return -1; int id = 0; // enumerate all video capture devices ICreateDevEnum *pCreateDevEnum; HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum); if (hr != NOERROR) return -1; IEnumMoniker *pEm; hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0); if (hr != NOERROR) return -1; pEm->Reset(); ULONG cFetched; IMoniker *pM; while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK) { IPropertyBag *pBag; hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag); if(SUCCEEDED(hr)) { VARIANT var; var.vt = VT_BSTR; hr = pBag->Read(L"FriendlyName", &var, NULL); if (hr == NOERROR) { id++; (long)SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)var.bstrVal); SysFreeString(var.bstrVal); } pBag->Release(); } pM->Release(); } return id; } /* 把当前工作的滤波器链表保存到*.grf */ void CVMR_Capture::SaveGraph(CString wFileName) { HRESULT hr; { IStorage* pStorage=NULL; // First, create a document file that will hold the GRF file hr = ::StgCreateDocfile(wFileName.AllocSysString(), STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &pStorage); if (FAILED(hr)) { AfxMessageBox(TEXT("Can not create a document")); return; } // Next, create a stream to store. WCHAR wszStreamName[] = L"ActiveMovieGraph"; IStream *pStream; hr = pStorage->CreateStream(wszStreamName,STGM_WRITE|STGM_CREATE|STGM_SHARE_EXCLUSIVE, 0, 0, &pStream); if(FAILED(hr)) { AfxMessageBox(TEXT("Can not create a stream")); pStorage->Release(); return; } // The IpersistStream::Save method converts a stream // into a persistent object. IPersistStream *pPersist = NULL; m_pGB->QueryInterface(IID_IPersistStream, reinterpret_cast<void**>(&pPersist)); hr = pPersist->Save(pStream, TRUE); pStream->Release(); pPersist->Release(); if(SUCCEEDED(hr)) { hr = pStorage->Commit(STGC_DEFAULT); if (FAILED(hr)) { AfxMessageBox(TEXT("can not store it")); } } pStorage->Release(); } }